home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / Advanced I⁄O v2.3 / Advanced i⁄o / endian_io.cc < prev    next >
Text File  |  1996-02-02  |  9KB  |  298 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *            Modified File class
  6.  *    to read integers of various sizes taking the byte order
  7.  *                into account
  8.  *              and bit-stream IO
  9.  *
  10.  * $Id: endian_io.cc,v 2.1 1995/03/02 18:06:20 oleg Exp oleg $
  11.  *
  12.  ************************************************************************
  13.  */
  14.  
  15. #ifdef __GNUC__
  16. #pragma implementation
  17. #endif
  18.  
  19. #include "endian_io.h"
  20. #include "std.h"
  21.  
  22. /*
  23.  *------------------------------------------------------------------------
  24.  *               Error handling
  25.  */
  26.  
  27. void EndianIOData::error(const char *descr)
  28. {
  29.   if( eof() )
  30.     _error("'%s' failed because of the End-Of-File",descr);
  31.   perror(descr);
  32.   _error("Aborted due to the I/O error above");
  33. }
  34.  
  35. void EndianIOData::assert_open(const char * file_name)
  36. {
  37.   if( good() )
  38.     return;
  39.   perror("Opening error");
  40.   _error("Failed to open file '%s' because of the error above",file_name);
  41. }
  42.  
  43. /*
  44.  *------------------------------------------------------------------------
  45.  *            Opening/closing
  46.  *
  47.  * Note that when the EndianIO stream is shared, it shares also the i/o
  48.  * buffer with the existing stream. Care should be taken
  49.  * to not destroy the buffer when the attached stream is closed/destroyed.
  50.  * The situation is similar to what happens when a file handle is
  51.  * duplicated with the system function dup(). Note that creating a new
  52.  * file buffer for the attached stream based on the dup()-ed file
  53.  * handle is not enough. Indeed, the old (sample) stream could've read
  54.  * some data ahead in its buffer, so the system file pointer would not
  55.  * correspond to data that were actually read (and consumed) by the
  56.  * user. This situation can be partly remedied using a fstream::sync()
  57.  * function (which sets the system file pointer to correspond to what
  58.  * actually was read from the stream and discards the read-ahead data).
  59.  * However, when the attached stream reads from the file and advances the file
  60.  * pointer, the user of the original stream should be able to recognize
  61.  * the fact that the file was read by another stream. This problem cannot
  62.  * be solved withoud sharing the file buffer itself.
  63.  */
  64.  
  65. void EndianIOData::share_with(EndianIOData& a_file)
  66. {
  67. #if 0            // Because of the stupid implementation of filebuf:
  68.   if( rdbuf() )        // filebuf is a part of the class fstream in GCC
  69.     delete rdbuf();        // Destroy the old stream buffer
  70. #endif
  71.   init(a_file.rdbuf());            // Share the buffer of a_file
  72.   byte_order = a_file.byte_order;
  73.   shared = true;
  74.   clear();
  75. }
  76.  
  77.  
  78.                 // leave the stream open when it was attached
  79. void EndianIn::close(void)
  80. {
  81.   if( !was_shared() )
  82.     ifstream::close();
  83. }
  84.  
  85. void EndianOut::close(void)
  86. {
  87.   if( !was_shared() )
  88.     ofstream::close();
  89. }
  90.                 // If the stream was attached, detach it
  91.                 // from the buffer
  92. void EndianIOData::unshare(void)
  93. {
  94.   if( was_shared() )
  95.     init(0), shared=false;
  96. }
  97.  
  98. /*
  99.  *------------------------------------------------------------------------
  100.  *               Reading routines
  101.  */
  102.  
  103.                 // Read a SHORT (2 bytes) datum item
  104.                 // in the specified byte_order
  105. unsigned short int EndianIn::read_short(const char * op_description)
  106. {
  107.   unsigned char c1, c2;
  108.  
  109.   if( !get(c1) || !get(c2) ) // Read 2 consecutive bytes
  110.     error(op_description);
  111.  
  112.   if( q_MSBfirst() )
  113.     return (c1 << 8) | c2;
  114.   else
  115.     return (c2 << 8) | c1;
  116. }
  117.  
  118.                 // Read a LONG (4 bytes) datum item
  119.                 // in the specified byte_order
  120. unsigned long int EndianIn::read_long(const char * op_description)
  121. {
  122.   unsigned char c1, c2, c3, c4;
  123.  
  124.   if( !get(c1) || !get(c2) ||             // Read 4 consecutive bytes
  125.       !get(c3) || !get(c4) )
  126.     error(op_description);
  127.  
  128.   if ( q_MSBfirst() )
  129.     return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
  130.   else
  131.     return (c4 << 24) | (c3 << 16) | (c2 << 8) | c1;
  132. }
  133.  
  134.  
  135. /*
  136.  *------------------------------------------------------------------------
  137.  *            Service writing routines
  138.  */
  139.  
  140. void EndianOut::write_byte
  141.     (const int item, const char * op_description)
  142. {
  143.   if( !put(item) )
  144.     error(op_description);
  145. }
  146.  
  147.                 // Write out a short item as specified by
  148.                 // the byte_order
  149. void EndianOut::write_short
  150.     (const unsigned short item, const char * op_description)
  151. {
  152. #if 0                    // The following does NOT work
  153.   union {                // because it doesn't make sure
  154.     unsigned short int short_int;    // that c2 follows c1
  155.     struct { unsigned char c1,c2; } bytes;
  156.   } int_bytes;
  157.   int_bytes.short_int = item;
  158.   write_byte(int_bytes.bytes.c2,"write_short, 2nd byte");
  159.   write_byte(int_bytes.bytes.c1,"write_short, 1st byte");
  160. #endif
  161.   if( q_MSBfirst() )
  162.     write_byte((item>>8) & 0xff,"write_short, hi byte"),
  163.     write_byte(item & 0xff,"write_short, lo byte");
  164.   else
  165.     write_byte(item & 0xff,"write_short, lo byte"),
  166.     write_byte((item>>8) & 0xff,"write_short, hi byte");
  167. }
  168.  
  169.  
  170.                 // Write out a long item as specified by
  171.                 // the byte_order
  172. void EndianOut::write_long
  173.     (const unsigned long item, const char * op_description)
  174. {
  175.   register unsigned int t = item;
  176.   register int i;
  177.  
  178.   if( q_MSBfirst() )
  179.     for(i=24; i>=0; i-=8)
  180.       write_byte((t >> i) & 0xff,"write_long");
  181.   else
  182.     for(i=0; i<4; i++)
  183.       write_byte(t & 0xff,"write_long"), t >>= 8;
  184. }
  185.  
  186. /*
  187.  *------------------------------------------------------------------------
  188.  *        Reading/Writing signed short (16-bit) integers
  189.  *            using variable-size code
  190.  * The code is intended for writing a collection of short integers where many
  191.  * of them are rather small in value; still, big values can crop up at times,
  192.  * so we can't limit the size of the code to anything less than 16 bits.
  193.  * The code is a variation of a start-stop code described in Appendix A,
  194.  * "Variable-length representations of the integers" of the "Text Compression"
  195.  * book by T.Bell, J.Cleary and I.Witten, p.290-295. The present code
  196.  * features support for both negative and positive numbers and optimization
  197.  * using a fact that all numbers are no larger than 2^15-1 in abs value
  198.  * and assumption that most of them smaller than 512 (in absolute value)
  199.  *
  200.  * Specifically, the code is as follows:
  201.  *  For numbers not exceeding 63 in abs value, the code is
  202.  *    0 sign-bit 6-bits-of-abs-value    (most significant bit first)
  203.  *  For numbers within the range [64,511+64] in abs value, the code is
  204.  *    10 sign-bit 9-bits-of-(abs-value - 64)
  205.  *  For numbers within the range [64+512,4095+512+64] in abs value, the code is
  206.  *    110 sign-bit 12-bits-of-(abs-value - 576)
  207.  *  For bigger numbers, the code is
  208.  *    111 16-bits-of-value
  209.  * Thus the penalty is at most 3 extra bits (comparing with the plain 16-bit
  210.  * encoding) for *very* big numbers.
  211.  */
  212.  
  213.              // Write a signed short integer 
  214.             // using a variable size code
  215. void BitOut::put_short(short item)
  216. {
  217.   int abs_val = abs(item);
  218.   if( !(abs_val & ~63) )        // small number
  219.   {
  220.     put_bit(0);
  221.     put_bit(item < 0);
  222.     for(register int i=32; i!=0; i>>=1)
  223.       put_bit(abs_val & i ? 1 : 0);        // write 6 bits with MSB first
  224.     return;
  225.   }
  226.  
  227.   if( !((abs_val-=64) & ~511) )
  228.   {
  229.     put_bit(1);
  230.     put_bit(0);
  231.     put_bit(item < 0);
  232.     for(register int i=256; i!=0; i>>=1)
  233.       put_bit(abs_val & i ? 1 : 0);
  234.     return;
  235.   }
  236.  
  237.   if( !((abs_val-=512) & ~4095) )
  238.   {
  239.     put_bit(1);
  240.     put_bit(1);
  241.     put_bit(0);
  242.     put_bit(item < 0);
  243.     for(register int i=2048; i!=0; i>>=1)
  244.       put_bit(abs_val & i ? 1 : 0);
  245.     return;
  246.   }
  247.   
  248.   put_bit(1);                // Very big number: default case
  249.   put_bit(1);
  250.   put_bit(1);                // Write all 16 bits with MSB first
  251.   for(register unsigned int i=(1<<15); i!=0; i>>=1)
  252.     put_bit(item & i ? 1 : 0);
  253.     
  254. }
  255.  
  256.             // Get a short integer that was written
  257.             // using a variable size code
  258. short BitIn::get_short(void)
  259. {
  260.  
  261.   if( !get_bit() )            // If the first bit turns out zero
  262.   {
  263.     const bool neg_sign = get_bit();
  264.     int val = 0;
  265.     for(register int i=32; i!=0; i>>=1)        // Read 6 bits of abs value
  266.       if( get_bit() )                // (MSB first)
  267.         val |= i;
  268.     return neg_sign ? -val : val;
  269.   }
  270.   
  271.   if( !get_bit() )            // First bit was 1, second is 0
  272.   {
  273.     const bool neg_sign = get_bit();
  274.     int val = 0;
  275.     for(register int i=256; i!=0; i>>=1)
  276.       if( get_bit() )
  277.         val |= i;
  278.     return val += 64, neg_sign ? -val : val;
  279.   }
  280.  
  281.   if( !get_bit() )            // First two bits were 1, third is 0
  282.   {
  283.     const bool neg_sign = get_bit();
  284.     int val = 0;
  285.     for(register int i=2048; i!=0; i>>=1)
  286.       if( get_bit() )
  287.         val |= i;
  288.     return val += 64+512, neg_sign ? -val : val;
  289.   }
  290.   
  291.   int val = 0;                // First three bits were all ones
  292.   for(register unsigned int i=(1<<15); i!=0; i>>=1)
  293.     if( get_bit() )
  294.       val |= i;
  295.   return val;
  296. }
  297.  
  298.